home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Utilities / Unix / top-v0_3 / Source / machine / m_sunos5.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-02-13  |  22.9 KB  |  874 lines

  1. /*
  2.  * top - a top users display for Unix
  3.  *
  4.  * SYNOPSIS:  Any Sun running SunOS 5.x (Solaris 2.x)
  5.  *
  6.  * DESCRIPTION:
  7.  * This is the machine-dependent module for SunOS 5.x (Solaris 2).
  8.  * There is some support for MP architectures.
  9.  * This makes top work on the following systems:
  10.  *         SunOS 5.0 (not tested)
  11.  *         SunOS 5.1
  12.  *         SunOS 5.2
  13.  *         SunOS 5.3
  14.  *
  15.  *     Tested on a SPARCclassic with SunOS 5.1, using gcc-2.3.3, and
  16.  *     SPARCsystem 600 with SunOS 5.2, using Sun C
  17.  *
  18.  * LIBS: -lelf -lkvm
  19.  *
  20.  * CFLAGS: -DHAVE_GETOPT
  21.  *
  22.  *
  23.  * AUTHORS:      Torsten Kasch      <torsten@techfak.uni-bielefeld.de>
  24.  *               Robert Boucher     <boucher@sofkin.ca>
  25.  * CONTRIBUTORS: Marc Cohen         <marc@aai.com>
  26.  *               Charles Hedrick    <hedrick@geneva.rutgers.edu>
  27.  *               William L. Jones     <jones@chpc>
  28.  *               Petri Kutvonen     <kutvonen@cs.helsinki.fi>
  29.  *               Casper Dik         <casper@fwi.uva.nl>
  30.  *               Tim Pugh           <tpugh@oce.orst.edu>
  31.  */
  32. #define _KMEMUSER
  33. #include "top.h"
  34. #include "machine.h"
  35. #include "utils.h"
  36. #include <stdio.h>
  37. #include <fcntl.h>
  38. #include <unistd.h>
  39. #include <stdlib.h>
  40. #include <errno.h>
  41. #include <dirent.h>
  42. #include <nlist.h>
  43. #include <string.h>
  44. #include <kvm.h>
  45. #include <sys/types.h>
  46. #include <sys/param.h>
  47. #include <sys/signal.h>
  48. #include <sys/fault.h>
  49. #include <sys/sysinfo.h>
  50. #include <sys/sysmacros.h>
  51. #include <sys/syscall.h>
  52. #include <sys/user.h>
  53. #include <sys/proc.h>
  54. #include <sys/procfs.h>
  55. #include <sys/vm.h>
  56. #include <sys/var.h>
  57. #include <sys/cpuvar.h>
  58. #include <sys/file.h>
  59. #include <sys/time.h>
  60. #include <sys/priocntl.h>
  61. #include <sys/tspriocntl.h>
  62. #include <sys/processor.h>
  63. #include <vm/anon.h>
  64. #include <math.h>
  65.  
  66. #define UNIX "/dev/ksyms"
  67. #define KMEM "/dev/kmem"
  68. #define PROCFS "/proc"
  69. #define CPUSTATES     5
  70. #ifndef PRIO_MIN
  71. #define PRIO_MIN    -20
  72. #endif
  73. #ifndef PRIO_MAX
  74. #define PRIO_MAX    20
  75. #endif
  76.  
  77. #ifndef FSCALE
  78. #define FSHIFT  8        /* bits to right of fixed binary point */
  79. #define FSCALE  (1<<FSHIFT)
  80. #endif /* FSCALE */
  81.  
  82. #define loaddouble(la) ((double)(la) / FSCALE)
  83. #define dbl_align(x)    (((unsigned long)(x)+(sizeof(double)-1)) & \
  84.                         ~(sizeof(double)-1))
  85. #ifdef SOLARIS24
  86.     /*
  87.      * snarfed from <sys/procfs.h>:
  88.      * The following percent numbers are 16-bit binary
  89.      * fractions [0 .. 1] with the binary point to the
  90.      * right of the high-order bit (one == 0x8000)
  91.      */
  92. #define percent_cpu(pp) (((double)pp->pr_pctcpu)/0x8000*100)
  93. #define weighted_cpu(pp) (*(double *)dbl_align(pp->pr_filler))
  94. #else
  95. #define percent_cpu(pp) (*(double *)dbl_align(&pp->pr_filler[0]))
  96. #define weighted_cpu(pp) (*(double *)dbl_align(&pp->pr_filler[2]))
  97. #endif
  98.  
  99. /* NOTE:  this next macro assumes that PAGESHIFT is 12---i.e.: a page is 4K.
  100.    This is a blatantly general assumption and ought to be fixed */
  101. #define pagetok(size) ((size)<<2)
  102.  
  103. /* definitions for indices in the nlist array */
  104. #define X_AVENRUN         0
  105. #define X_MPID             1
  106. #define X_CPU             2
  107. #define X_V             3
  108. #define X_NPROC             4
  109. #define X_ANONINFO         5
  110. #define X_FREEMEM         6
  111. #define X_MAXMEM         7
  112. #define X_AVAILRMEM         8
  113. #define X_SWAPFS_MINFREE     9
  114. #define X_NCPUS               10
  115.  
  116. static struct nlist nlst[] =
  117. {
  118.   {"avenrun"},            /* 0 */
  119.   {"mpid"},            /* 1 */
  120.   {"cpu"},            /* 2 */
  121.   {"v"},            /* 3 */
  122.   {"nproc"},            /* 4 */
  123.   {"anoninfo"},            /* 5 */
  124.   {"freemem"},            /* 6 */
  125.   {"maxmem"},            /* 7 */
  126.   {"availrmem"},        /* 8 */
  127.   {"swapfs_minfree"},        /* 9 */
  128.   {"ncpus"},            /* 10 */
  129.   {0}
  130. };
  131.  
  132. static unsigned long avenrun_offset;
  133. static unsigned long mpid_offset;
  134. static unsigned long *cpu_offset;
  135. static unsigned long nproc_offset;
  136. static unsigned long freemem_offset;
  137. static unsigned long maxmem_offset;
  138. static unsigned long availrmem_offset;
  139. static unsigned long swapfs_minfree_offset;
  140. static unsigned long anoninfo_offset;
  141.  
  142. /* get_process_info passes back a handle.  This is what it looks like: */
  143. struct handle
  144.   {
  145.     struct prpsinfo **next_proc;/* points to next valid proc pointer */
  146.     int remaining;        /* number of pointers remaining */
  147.   };
  148.  
  149. /*
  150.  * Structure for keeping track of CPU times from last time around
  151.  * the program.  We keep these things in a hash table, which is
  152.  * recreated at every cycle.
  153.  */
  154. struct oldproc
  155.   {
  156.     pid_t oldpid;
  157.     double oldtime;
  158.     double oldpct;
  159.   };
  160. int oldprocs;            /* size of table */
  161. #define HASH(x) ((x << 1) % oldprocs)
  162.  
  163. /*
  164.  * GCC assumes that all doubles are aligned.  Unfortunately it
  165.  * doesn't round up the structure size to be a multiple of 8.
  166.  * Thus we'll get a coredump when going through array.  The
  167.  * following is a size rounded up to 8.
  168.  */
  169. #define PRPSINFOSIZE dbl_align(sizeof(struct prpsinfo))
  170. /*
  171.  *  These definitions control the format of the per-process area
  172.  */
  173. static char header[] =
  174. "  PID X        PRI NICE  SIZE   RES STATE   TIME   WCPU    CPU COMMAND";
  175. /* 0123456   -- field to fill in starts at header+6 */
  176. #define UNAME_START 6
  177.  
  178. #define Proc_format \
  179.         "%5d %-8.8s %3d %4d %5s %5s %-5s %6s %5.2f%% %5.2f%% %s"
  180.  
  181. /* process state names for the "STATE" column of the display */
  182. /* the extra nulls in the string "run" are for adding a slash and
  183.    the processor number when needed */
  184. char *state_abbrev[] =
  185. {"", "sleep", "run", "zombie", "stop", "start", "cpu", "swap"};
  186.  
  187. int process_states[8];
  188. char *procstatenames[] =
  189. {
  190.   "", " sleeping, ", " running, ", " zombie, ", " stopped, ",
  191.   " starting, ", " on cpu, ", " swapped, ",
  192.   NULL
  193. };
  194.  
  195. int cpu_states[CPUSTATES];
  196. char *cpustatenames[] =
  197. {"idle", "user", "kernel", "iowait", "swap", NULL};
  198.  
  199. /* these are for detailing the memory statistics */
  200. int memory_stats[5];
  201. char *memorynames[] =
  202. {"K real, ", "K active, ", "K free, ", "K swap, ", "K free swap", NULL};
  203.  
  204. kvm_t *kd;
  205. static DIR *procdir;
  206. static int nproc;
  207. static int ncpus;
  208.  
  209. /* these are for keeping track of the proc array */
  210. static int bytes;
  211. static struct prpsinfo *pbase;
  212. static struct prpsinfo **pref;
  213. static struct oldproc *oldbase;
  214.  
  215. /* useful externals */
  216. extern int errno;
  217. extern char *sys_errlist[];
  218. extern char *myname;
  219. extern int check_nlist ();
  220. extern int gettimeofday ();
  221. extern int getkval ();
  222. extern void perror ();
  223. extern void getptable ();
  224. extern void quit ();
  225. extern int nlist ();
  226.  
  227. int
  228. machine_init (struct statics *statics)
  229.   {
  230.     static struct var v;
  231.     struct oldproc *op, *endbase;
  232.     int i;
  233.     int offset;
  234.     processor_info_t pi;
  235.  
  236.     /* perform the kvm_open */
  237.     kd = kvm_open (NULL, NULL, NULL, O_RDONLY, "top");
  238.  
  239.     /* turn off super user privs */
  240.     seteuid(getuid());
  241.  
  242.     /* fill in the statics information */
  243.     statics->procstate_names = procstatenames;
  244.     statics->cpustate_names = cpustatenames;
  245.     statics->memory_names = memorynames;
  246.  
  247.     /* test kvm_open return value */
  248.     if (kd == NULL)
  249.       {
  250.     perror ("kvm_open");
  251.     return (-1);
  252.       }
  253.     if (kvm_nlist (kd, nlst) < 0)
  254.       {
  255.     perror ("kvm_nlist");
  256.     return (-1);
  257.       }
  258.     if (check_nlist (nlst) != 0)
  259.       return (-1);
  260.  
  261.     /* NPROC Tuning parameter for max number of processes */
  262.     (void) getkval (nlst[X_V].n_value, &v, sizeof (struct var), nlst[X_V].n_name);
  263.     nproc = v.v_proc;
  264.  
  265.     /* stash away certain offsets for later use */
  266.     mpid_offset = nlst[X_MPID].n_value;
  267.     nproc_offset = nlst[X_NPROC].n_value;
  268.     avenrun_offset = nlst[X_AVENRUN].n_value;
  269.     anoninfo_offset = nlst[X_ANONINFO].n_value;
  270.     freemem_offset = nlst[X_FREEMEM].n_value;
  271.     maxmem_offset = nlst[X_MAXMEM].n_value;
  272.     availrmem_offset = nlst[X_AVAILRMEM].n_value;
  273.     swapfs_minfree_offset = nlst[X_SWAPFS_MINFREE].n_value;
  274.  
  275.     (void) getkval (nlst[X_NCPUS].n_value, (int *) (&ncpus),
  276.             sizeof (ncpus), "ncpus");
  277.  
  278.     cpu_offset = (unsigned long *) malloc (ncpus * sizeof (unsigned long));
  279.     for (i = offset = 0; i < ncpus; offset += sizeof(unsigned long)) {
  280.         (void) getkval (nlst[X_CPU].n_value + offset,
  281.                         &cpu_offset[i], sizeof (unsigned long),
  282.                         nlst[X_CPU].n_name );
  283.         if (cpu_offset[i] != 0)
  284.             i++;
  285.     }
  286.  
  287.  
  288.     /* allocate space for proc structure array and array of pointers */
  289.     bytes = nproc * PRPSINFOSIZE;
  290.     pbase = (struct prpsinfo *) malloc (bytes);
  291.     pref = (struct prpsinfo **) malloc (nproc * sizeof (struct prpsinfo *));
  292.     oldbase = (struct oldproc *) malloc (2 * nproc * sizeof (struct oldproc));
  293.  
  294.  
  295.     /* Just in case ... */
  296.     if (pbase == (struct prpsinfo *) NULL || pref == (struct prpsinfo **) NULL
  297.     || oldbase == (struct oldproc *) NULL)
  298.       {
  299.     fprintf (stderr, "%s: can't allocate sufficient memory\n", myname);
  300.     return (-1);
  301.       }
  302.  
  303.     oldprocs = 2 * nproc;
  304.     endbase = oldbase + oldprocs;
  305.     for (op = oldbase; op < endbase; op++)
  306.       op->oldpid = -1;
  307.  
  308.     if (!(procdir = opendir (PROCFS)))
  309.       {
  310.     (void) fprintf (stderr, "Unable to open %s\n", PROCFS);
  311.     return (-1);
  312.       }
  313.  
  314.     if (chdir (PROCFS))
  315.       {                /* handy for later on when we're reading it */
  316.     (void) fprintf (stderr, "Unable to chdir to %s\n", PROCFS);
  317.     return (-1);
  318.       }
  319.  
  320.     /* all done! */
  321.     return (0);
  322.   }
  323.  
  324.  
  325. char *
  326. format_header (register char *uname_field)
  327. {
  328.   register char *ptr;
  329.  
  330.   ptr = header + UNAME_START;
  331.   while (*uname_field != '\0')
  332.     *ptr++ = *uname_field++;
  333.  
  334.   return (header);
  335. }
  336.  
  337. void
  338. get_system_info (struct system_info *si)
  339. {
  340.   long avenrun[3];
  341.   struct cpu cpu;
  342.   static int freemem;
  343.   static int maxmem;
  344.   static int availrmem;
  345.   static int swapfs_minfree;
  346.   struct anoninfo anoninfo;
  347.   static long cp_time[CPUSTATES];
  348.   static long cp_old[CPUSTATES];
  349.   static long cp_diff[CPUSTATES];
  350.   register int j, i;
  351.  
  352.   /* get the cp_time array */
  353.   for (j = 0; j < CPUSTATES; j++)
  354.     cp_time[j] = 0L;
  355.  
  356.   for (i = 0; i < ncpus; i++)
  357.     if (cpu_offset[i] != 0)
  358.     {
  359.       (void) getkval (cpu_offset[i], &cpu, sizeof (struct cpu), "cpu");
  360.       for (j = 0; j < CPUSTATES-1; j++)
  361.     cp_time[j] += (long) cpu.cpu_stat.cpu_sysinfo.cpu[j];
  362.       cp_time[CPUSTATES-2] += (long) cpu.cpu_stat.cpu_sysinfo.wait[W_IO] +
  363.                               (long) cpu.cpu_stat.cpu_sysinfo.wait[W_PIO];
  364.       cp_time[CPUSTATES-1] += (long) cpu.cpu_stat.cpu_sysinfo.wait[W_SWAP];
  365.     }
  366.  
  367.   /* convert cp_time counts to percentages */
  368.   (void) percentages (CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
  369.  
  370.   /* get mpid -- process id of last process */
  371.   (void) getkval (mpid_offset, &(si->last_pid), sizeof (si->last_pid), "mpid");
  372.  
  373.   /* get load average array */
  374.   (void) getkval (avenrun_offset, (int *) avenrun, sizeof (avenrun), "avenrun");
  375.  
  376.   /* convert load averages to doubles */
  377.   for (i = 0; i < 3; i++)
  378.     si->load_avg[i] = loaddouble (avenrun[i]);
  379.  
  380.   /* get system wide main memory usage structure */
  381.   (void) getkval (freemem_offset, (int *) (&freemem), sizeof (freemem), "freemem");
  382.   (void) getkval (maxmem_offset, (int *) (&maxmem), sizeof (maxmem), "maxmem");
  383.   memory_stats[0] = pagetok (maxmem);
  384.   memory_stats[1] = 0;
  385.   memory_stats[2] = pagetok (freemem);
  386.   (void) getkval (anoninfo_offset, (int *) (&anoninfo), sizeof (anoninfo), "anoninfo");
  387.   (void) getkval (availrmem_offset, (int *) (&availrmem), sizeof (availrmem), "availrmem");
  388.   (void) getkval (swapfs_minfree_offset, (int *) (&swapfs_minfree), sizeof (swapfs_minfree), "swapfs_minfree");
  389.   memory_stats[3] = pagetok (anoninfo.ani_resv);
  390.   memory_stats[4] = pagetok (MAX ((int) (anoninfo.ani_max - anoninfo.ani_resv), 0) + availrmem - swapfs_minfree);
  391.  
  392.   /* set arrays and strings */
  393.   si->cpustates = cpu_states;
  394.   si->memory = memory_stats;
  395. }
  396.  
  397. static struct handle handle;
  398.  
  399. caddr_t
  400. get_process_info (
  401.            struct system_info *si,
  402.            struct process_select *sel,
  403.            int (*compare) ())
  404. {
  405.   register int i;
  406.   register int total_procs;
  407.   register int active_procs;
  408.   register struct prpsinfo **prefp;
  409.   register struct prpsinfo *pp;
  410.  
  411.   /* these are copied out of sel for speed */
  412.   int show_idle;
  413.   int show_system;
  414.   int show_uid;
  415.  
  416.   /* Get current number of processes */
  417.   (void) getkval (nproc_offset, (int *) (&nproc), sizeof (nproc), "nproc");
  418.  
  419.   /* read all the proc structures */
  420.   getptable (pbase);
  421.  
  422.   /* get a pointer to the states summary array */
  423.   si->procstates = process_states;
  424.  
  425.   /* set up flags which define what we are going to select */
  426.   show_idle = sel->idle;
  427.   show_system = sel->system;
  428.   show_uid = sel->uid != -1;
  429.  
  430.   /* count up process states and get pointers to interesting procs */
  431.   total_procs = 0;
  432.   active_procs = 0;
  433.   (void) memset (process_states, 0, sizeof (process_states));
  434.   prefp = pref;
  435.  
  436.   for (pp = pbase, i = 0; i < nproc;
  437.        i++, pp = (struct prpsinfo *) ((char *) pp + PRPSINFOSIZE))
  438.     {
  439.     /*
  440.      *  Place pointers to each valid proc structure in pref[].
  441.      *  Process slots that are actually in use have a non-zero
  442.      *  status field.  Processes with SSYS set are system
  443.      *  processes---these get ignored unless show_sysprocs is set.
  444.      */
  445.       if (pp->pr_state != 0 &&
  446.       (show_system || ((pp->pr_flag & SSYS) == 0)))
  447.     {
  448.       total_procs++;
  449.       process_states[pp->pr_state]++;
  450.       if ((!pp->pr_zomb) &&
  451.           (show_idle || percent_cpu (pp) || (pp->pr_state == SRUN) || (pp->pr_state == SONPROC)) &&
  452.           (!show_uid || pp->pr_uid == (uid_t) sel->uid))
  453.         {
  454.           *prefp++ = pp;
  455.           active_procs++;
  456.         }
  457.     }
  458.     }
  459.  
  460.   /* if requested, sort the "interesting" processes */
  461.   if (compare != NULL)
  462.     qsort ((char *) pref, active_procs, sizeof (struct prpsinfo *), compare);
  463.  
  464.   /* remember active and total counts */
  465.   si->p_total = total_procs;
  466.   si->p_active = active_procs;
  467.  
  468.   /* pass back a handle */
  469.   handle.next_proc = pref;
  470.   handle.remaining = active_procs;
  471.   return ((caddr_t) & handle);
  472. }
  473.  
  474. char fmt[MAX_COLS];            /* static area where result is built */
  475.  
  476. char *
  477. format_next_process (
  478.               caddr_t handle,
  479.               char *(*get_userid) ())
  480. {
  481.   register struct prpsinfo *pp;
  482.   struct handle *hp;
  483.   register long cputime;
  484.   register double pctcpu;
  485.  
  486.   /* find and remember the next proc structure */
  487.   hp = (struct handle *) handle;
  488.   pp = *(hp->next_proc++);
  489.   hp->remaining--;
  490.  
  491.   /* get the cpu usage and calculate the cpu percentages */
  492.   cputime = pp->pr_time.tv_sec;
  493.   pctcpu = percent_cpu (pp);
  494.  
  495.   /* format this entry */
  496.   sprintf (fmt,
  497.        Proc_format,
  498.        pp->pr_pid,
  499.        (*get_userid) (pp->pr_uid),
  500.        pp->pr_pri - PZERO,
  501.        pp->pr_nice - NZERO,
  502.        format_k(pp->pr_bysize / 1024),
  503.        format_k(pp->pr_byrssize / 1024),
  504.        state_abbrev[pp->pr_state],
  505.        format_time(cputime),
  506.        weighted_cpu (pp),
  507.        pctcpu,
  508.        pp->pr_fname);
  509.  
  510.   /* return the result */
  511.   return (fmt);
  512. }
  513.  
  514. /*
  515.  * check_nlist(nlst) - checks the nlist to see if any symbols were not
  516.  *        found.  For every symbol that was not found, a one-line
  517.  *        message is printed to stderr.  The routine returns the
  518.  *        number of symbols NOT found.
  519.  */
  520. int
  521. check_nlist (register struct nlist *nlst)
  522. {
  523.   register int i;
  524.  
  525.   /* check to see if we got ALL the symbols we requested */
  526.   /* this will write one line to stderr for every symbol not found */
  527.  
  528.   i = 0;
  529.   while (nlst->n_name != NULL)
  530.     {
  531.       if (nlst->n_type == 0)
  532.     {
  533.       /* this one wasn't found */
  534.       fprintf (stderr, "kernel: no symbol named `%s'\n", nlst->n_name);
  535.       i = 1;
  536.     }
  537.       nlst++;
  538.     }
  539.   return (i);
  540. }
  541.  
  542.  
  543. /*
  544.  *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
  545.  *    "offset" is the byte offset into the kernel for the desired value,
  546.  *      "ptr" points to a buffer into which the value is retrieved,
  547.  *      "size" is the size of the buffer (and the object to retrieve),
  548.  *      "refstr" is a reference string used when printing error meessages,
  549.  *        if "refstr" starts with a '!', then a failure on read will not
  550.  *          be fatal (this may seem like a silly way to do things, but I
  551.  *          really didn't want the overhead of another argument).
  552.  *
  553.  */
  554. int
  555. getkval (unsigned long offset,
  556.      int *ptr,
  557.      int size,
  558.      char *refstr)
  559. {
  560.   if (kvm_read (kd, offset, (char *) ptr, size) != size)
  561.     {
  562.       if (*refstr == '!')
  563.     {
  564.       return (0);
  565.     }
  566.       else
  567.     {
  568.       fprintf (stderr, "top: kvm_read for %s: %s\n", refstr, sys_errlist[errno]);
  569.       quit (23);
  570.     }
  571.     }
  572.   return (1);
  573.  
  574. }
  575.  
  576. /* comparison routine for qsort */
  577.  
  578. /*
  579.  *  proc_compare - comparison function for "qsort"
  580.  *    Compares the resource consumption of two processes using five
  581.  *      distinct keys.  The keys (in descending order of importance) are:
  582.  *      percent cpu, cpu ticks, state, resident set size, total virtual
  583.  *      memory usage.  The process states are ordered as follows (from least
  584.  *      to most important):  WAIT, zombie, sleep, stop, start, run.  The
  585.  *      array declaration below maps a process state index into a number
  586.  *      that reflects this ordering.
  587.  */
  588.  
  589.  
  590. unsigned char sorted_state[] =
  591. {
  592.   0,                /* not used        */
  593.   3,                /* sleep        */
  594.   6,                /* run            */
  595.   2,                /* zombie        */
  596.   4,                /* stop            */
  597.   5,                /* start        */
  598.   7,                /* run on a processor   */
  599.   1                /* being swapped (WAIT)    */
  600. };
  601. int
  602. proc_compare (
  603.            struct prpsinfo **pp1,
  604.            struct prpsinfo **pp2)
  605.   {
  606.     register struct prpsinfo *p1;
  607.     register struct prpsinfo *p2;
  608.     register long result;
  609.     double dresult;
  610.  
  611.     /* remove one level of indirection */
  612.     p1 = *pp1;
  613.     p2 = *pp2;
  614.  
  615.     /* compare percent cpu (pctcpu) */
  616.     dresult = percent_cpu (p2) - percent_cpu (p1);
  617.     if (dresult != 0.0)
  618.       {
  619.     if (dresult > 0.0)
  620.       return 1;
  621.     else
  622.       return -1;
  623.       }
  624.     {
  625.       /* use cpticks to break the tie */
  626.       if ((result = p2->pr_time.tv_sec - p1->pr_time.tv_sec) == 0)
  627.     {
  628.       /* use process state to break the tie */
  629.       if ((result = (long) (sorted_state[p2->pr_state] -
  630.                 sorted_state[p1->pr_state])) == 0)
  631.         {
  632.           /* use priority to break the tie */
  633.           if ((result = p2->pr_oldpri - p1->pr_oldpri) == 0)
  634.         {
  635.           /* use resident set size (rssize) to break the tie */
  636.           if ((result = p2->pr_rssize - p1->pr_rssize) == 0)
  637.             {
  638.               /* use total memory to break the tie */
  639.               result = (p2->pr_size - p1->pr_size);
  640.             }
  641.         }
  642.         }
  643.     }
  644.     }
  645.     return (result);
  646.   }
  647.  
  648. /*
  649. get process table
  650.  V.4 only has a linked list of processes so we want to follow that
  651.  linked list, get all the process structures, and put them in our own
  652.  table
  653. */
  654. void
  655. getptable (struct prpsinfo *baseptr)
  656. {
  657.   struct prpsinfo *currproc;    /* pointer to current proc structure    */
  658.   int numprocs = 0;
  659.   int i;
  660.   struct dirent *direntp;
  661.   struct oldproc *op;
  662.   static struct timeval lasttime =
  663.   {0, 0};
  664.   struct timeval thistime;
  665.   double timediff;
  666.   double alpha, beta;
  667.   struct oldproc *endbase;
  668.   char d_name[12];
  669.  
  670.   gettimeofday (&thistime);
  671.   /*
  672.    * To avoid divides, we keep times in nanoseconds.  This is
  673.    * scaled by 1e7 rather than 1e9 so that when we divide we
  674.    * get percent.
  675.    */
  676.   if (lasttime.tv_sec)
  677.     timediff = ((double) thistime.tv_sec * 1.0e7 +
  678.         ((double) thistime.tv_usec * 10.0)) -
  679.       ((double) lasttime.tv_sec * 1.0e7 +
  680.        ((double) lasttime.tv_usec * 10.0));
  681.   else
  682.     timediff = 1.0e7;
  683.  
  684.   /*
  685.      * constants for exponential average.  avg = alpha * new + beta * avg
  686.      * The goal is 50% decay in 30 sec.  However if the sample period
  687.      * is greater than 30 sec, there's not a lot we can do.
  688.      */
  689.   if (timediff < 30.0e7)
  690.     {
  691.       alpha = 0.5 * (timediff / 30.0e7);
  692.       beta = 1.0 - alpha;
  693.     }
  694.   else
  695.     {
  696.       alpha = 0.5;
  697.       beta = 0.5;
  698.     }
  699.  
  700.   endbase = oldbase + oldprocs;
  701.   currproc = baseptr;
  702.  
  703.   /* before reading /proc files, turn on root privs */
  704.   /* (we don't care if this fails since it will be caught later) */
  705.   seteuid(0);
  706.   rewinddir (procdir);
  707.   while( (direntp = readdir (procdir)) != NULL)
  708.     {
  709.       int fd;
  710.  
  711.       if(direntp->d_ino <= 64)
  712.           continue;
  713.  
  714.       /* use file name = (direntp->d_ino - 64), not direntp->d_name.
  715.        * 1/12/96 tpugh
  716.        */
  717.       sprintf(d_name,"%i",direntp->d_ino - 64);
  718.       if ((fd = open (d_name, O_RDONLY)) < 0)
  719.       {
  720.           perror("top getptable open");
  721.         continue;
  722.       }
  723.  
  724.       if (ioctl (fd, PIOCPSINFO, currproc) < 0)
  725.     {
  726.       (void) close (fd);
  727.       continue;
  728.     }
  729.  
  730.       /*
  731.        * SVr4 doesn't keep track of CPU% in the kernel, so we have
  732.        * to do our own.  See if we've heard of this process before.
  733.        * If so, compute % based on CPU since last time.
  734.        */
  735.       op = oldbase + HASH (currproc->pr_pid);
  736.       while (1)
  737.     {
  738.       if (op->oldpid == -1)    /* not there */
  739.         break;
  740.       if (op->oldpid == currproc->pr_pid)
  741.         {            /* found old data */
  742. #ifndef SOLARIS24
  743.           percent_cpu (currproc) =
  744.         ((currproc->pr_time.tv_sec * 1.0e9 +
  745.           currproc->pr_time.tv_nsec)
  746.          - op->oldtime) / timediff;
  747. #endif
  748.           weighted_cpu (currproc) =
  749.         op->oldpct * beta + percent_cpu (currproc) * alpha;
  750.  
  751.           break;
  752.         }
  753.       op++;            /* try next entry in hash table */
  754.       if (op == endbase)    /* table wrapped around */
  755.         op = oldbase;
  756.     }
  757.  
  758.       /* Otherwise, it's new, so use all of its CPU time */
  759.       if (op->oldpid == -1)
  760.     {
  761. #ifdef SOLARIS24
  762.       weighted_cpu (currproc) =
  763.         percent_cpu (currproc);
  764. #else
  765.       if (lasttime.tv_sec)
  766.         {
  767.           percent_cpu (currproc) =
  768.         (currproc->pr_time.tv_sec * 1.0e9 +
  769.          currproc->pr_time.tv_nsec) / timediff;
  770.           weighted_cpu (currproc) =
  771.         percent_cpu (currproc);
  772.         }
  773.       else
  774.         {            /* first screen -- no difference is possible */
  775.           percent_cpu (currproc) = 0.0;
  776.           weighted_cpu (currproc) = 0.0;
  777.         }
  778. #endif
  779.     }
  780.  
  781.       numprocs++;
  782.       currproc = (struct prpsinfo *) ((char *) currproc + PRPSINFOSIZE);
  783.       (void) close (fd);
  784.     }
  785.  
  786.   /* turn off root privs */
  787.   seteuid(getuid());
  788.  
  789.   if (nproc != numprocs)
  790.     nproc = numprocs;
  791.  
  792.   /*
  793.    * Save current CPU time for next time around
  794.    * For the moment recreate the hash table each time, as the code
  795.    * is easier that way.
  796.    */
  797.   oldprocs = 2 * nproc;
  798.   endbase = oldbase + oldprocs;
  799.   for (op = oldbase; op < endbase; op++)
  800.     op->oldpid = -1;
  801.   for (i = 0, currproc = baseptr;
  802.        i < nproc;
  803.      i++, currproc = (struct prpsinfo *) ((char *) currproc + PRPSINFOSIZE))
  804.     {
  805.       /* find an empty spot */
  806.       op = oldbase + HASH (currproc->pr_pid);
  807.       while (1)
  808.     {
  809.       if (op->oldpid == -1)
  810.         break;
  811.       op++;
  812.       if (op == endbase)
  813.         op = oldbase;
  814.     }
  815.       op->oldpid = currproc->pr_pid;
  816.       op->oldtime = (currproc->pr_time.tv_sec * 1.0e9 +
  817.              currproc->pr_time.tv_nsec);
  818.       op->oldpct = weighted_cpu (currproc);
  819.     }
  820.   lasttime = thistime;
  821. }
  822.  
  823. /*
  824.  * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
  825.  *              the process does not exist.
  826.  *              It is EXTREMLY IMPORTANT that this function work correctly.
  827.  *              If top runs setuid root (as in SVR4), then this function
  828.  *              is the only thing that stands in the way of a serious
  829.  *              security problem.  It validates requests for the "kill"
  830.  *              and "renice" commands.
  831.  */
  832. uid_t
  833. proc_owner (pid_t pid)
  834. {
  835.   register struct prpsinfo *p;
  836.   int i;
  837.   for (i = 0, p = pbase; i < nproc;
  838.        i++, p = (struct prpsinfo *) ((char *) p + PRPSINFOSIZE)) {
  839.     if (p->pr_pid == pid)
  840.       return (p->pr_uid);
  841.   }
  842.   return (-1);
  843. }
  844.  
  845. int
  846. setpriority (int dummy, int who, int niceval)
  847. {
  848.   int scale;
  849.   int prio;
  850.   pcinfo_t pcinfo;
  851.   pcparms_t pcparms;
  852.   tsparms_t *tsparms;
  853.  
  854.   strcpy (pcinfo.pc_clname, "TS");
  855.   if (priocntl (0, 0, PC_GETCID, (caddr_t) & pcinfo) == -1)
  856.     return (-1);
  857.  
  858.   prio = niceval;
  859.   if (prio > PRIO_MAX)
  860.     prio = PRIO_MAX;
  861.   else if (prio < PRIO_MIN)
  862.     prio = PRIO_MIN;
  863.  
  864.   tsparms = (tsparms_t *) pcparms.pc_clparms;
  865.   scale = ((tsinfo_t *) pcinfo.pc_clinfo)->ts_maxupri;
  866.   tsparms->ts_uprilim = tsparms->ts_upri = -(scale * prio) / 20;
  867.   pcparms.pc_cid = pcinfo.pc_cid;
  868.  
  869.   if (priocntl (P_PID, who, PC_SETPARMS, (caddr_t) & pcparms) == -1)
  870.     return (-1);
  871.  
  872.   return (0);
  873. }
  874.